package com.baofu.paycore.service;

import com.baofu.paycore.biz.model.BackPayBO;
import com.baofu.paycore.biz.model.WebPayBO;
import com.baofu.paycore.biz.paycore.handler.impl.OfflineRechargeHandler;
import com.baofu.paycore.biz.paycore.handler.impl.RechargeHandler;
import com.baofu.paycore.biz.paycore.handler.impl.WebRechargeHandler;
import com.baofu.paycore.common.exception.ValidateException;
import com.baofu.paycore.common.utils.ExceptionUtil;
import com.baofu.paycore.manager.model.RechargeBO;
import com.baofu.paycore.manager.storer.RechargeOrderRepository;
import com.baofu.paycore.service.converter.BO2DTOConverter;
import com.baofu.paycore.service.facade.RechargeServiceFacade;
import com.baofu.paycore.service.facade.enums.PayCoreErrorCode;
import com.baofu.paycore.service.facade.enums.PayStatusEnums;
import com.baofu.paycore.service.facade.enums.ServiceCodeEnum;
import com.baofu.paycore.service.facade.model.WebPayResDTO;
import com.baofu.paycore.service.facade.model.recharge.RechargeConfirmDTO;
import com.baofu.paycore.service.facade.model.recharge.RechargeDTO;
import com.baofu.paycore.service.facade.model.recharge.RechargeResDTO;
import com.baofu.paycore.service.factory.impl.OfflineRechargeFactoryImpl;
import com.baofu.paycore.service.factory.impl.RechargeFactoryImpl;
import com.baofu.paycore.service.validate.impl.recharge.RechargeConfirmValidate;
import com.baofu.paycore.service.validate.impl.recharge.RechargeValidate;
import com.google.common.base.Strings;
import com.system.commons.result.Result;
import com.system.commons.utils.ParamValidate;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.slf4j.helpers.SystemMarker;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;

/**
 * 充值服务实现
 * <p>
 * 1、获取跳转外部支付渠道进行充值的URL
 * 2、后台充值接口
 * 3、线下充值接口
 * </p>
 * User: LZQ  Date: 2014/12/04 ProjectName: paycore Version: 5.0.0
 * User: kwl  Date: 2016/05/06 ProjectName: paycore Version: 5.0.0
 */
@Slf4j
@Service
public class RechargeService implements RechargeServiceFacade {

    /**
     * 充值/提现业务处理层
     */
    @Autowired
    private RechargeHandler rechargeHandler;

    /**
     * 充值校验
     */
    @Resource
    private RechargeValidate rechargeValidate;

    /**
     * 充值单处理器
     */
    @Autowired
    private RechargeFactoryImpl rechargeFactory;

    /**
     * 充值单处理器
     */
    @Autowired
    private OfflineRechargeFactoryImpl offlineRechargeFactory;

    /**
     * 充值/提现业务处理层
     */
    @Autowired
    private WebRechargeHandler webRechargeHandler;

    /**
     * 充值订单持久化服务
     */
    @Autowired
    private RechargeOrderRepository rechargeOrderRepository;

    /**
     * 线下打款充值订单处理handler
     */
    @Autowired
    private OfflineRechargeHandler offlineRechargeHandler;

    @Autowired
    private RechargeConfirmValidate rechargeConfirmValidate;

    /**
     * 获取跳转外部支付渠道进行充值的URL
     *
     * @param rechargeDTO 充值请求model
     * @return 跳转银行所需参数
     */
    @Override
    public Result<WebPayResDTO> webRecharge(RechargeDTO rechargeDTO, String transLogId) {
        long start = System.currentTimeMillis();
        Result<WebPayResDTO> result = null;
        boolean flag = false;

        try {
            MDC.put(SystemMarker.TRACE_LOG_ID, transLogId);

            log.info("web 充值请求, rechargeDTO{}", rechargeDTO);
            flag = rechargeValidate.lock(rechargeDTO);

            //1、请求对象参数校验
            rechargeValidate.paramsValidate(rechargeDTO);
            result = rechargeValidate.validateUniqueOrder(rechargeDTO, rechargeOrderRepository, WebPayResDTO.class);
            if (result != null) {
                return result;
            }
            //2、请求对象转换
            RechargeBO rechargeBO = rechargeFactory.build(rechargeDTO);
            rechargeBO.setTradeLogId(transLogId);

            //3、支付相关信息落地
            rechargeOrderRepository.addOrder(rechargeBO);

            //4、业务流程处理
            WebPayBO webPayBO = webRechargeHandler.asyncProcess(rechargeBO);

            //5、返回对象生成
            WebPayResDTO webPayResDTO = BO2DTOConverter.getWebPayResDTO(webPayBO, rechargeBO);
            result = new Result<>(webPayResDTO);

        } catch (Exception e) {

            log.error("call webRecharge error, EXCEPTION:{}", e);
            result = ExceptionUtil.doExceptionService(e, WebPayResDTO.class);
        } finally {
            log.info("web recharge 耗时:{},充值结果{}", System.currentTimeMillis() - start, result);
            rechargeValidate.release(rechargeDTO, flag);
            MDC.clear();
        }
        return result;
    }

    /**
     * 后台充值接口
     *
     * @param rechargeDTO 充值请求model
     * @return 充值结果
     */
    @Override
    public Result<RechargeResDTO> recharge(RechargeDTO rechargeDTO, String transLogId) {
        long start = System.currentTimeMillis();
        Result<RechargeResDTO> result = null;
        boolean flag = false;

        try {
            MDC.put(SystemMarker.TRACE_LOG_ID, transLogId);

            log.info("后台充值请求参数, rechargeDTO{}", rechargeDTO);
            flag = rechargeValidate.lock(rechargeDTO);

            //1、请求对象参数校验
            rechargeValidate.paramsValidate(rechargeDTO);
            result = rechargeValidate.validateUniqueOrder(rechargeDTO, rechargeOrderRepository, RechargeResDTO.class);
            if (result != null) {
                this.existOrderProcess(result);
                return result;
            }
            //2、请求对象转换
            RechargeBO rechargeBO = rechargeFactory.build(rechargeDTO);
            rechargeBO.setTradeLogId(transLogId);
            //3、支付相关信息落地
            rechargeOrderRepository.addOrder(rechargeBO);

            //4、业务流程处理
            BackPayBO backPayBO = rechargeHandler.process(rechargeBO);

            //5、返回对象生成
            RechargeResDTO rechargeResult = BO2DTOConverter.getRechargeResDTO(backPayBO);
            result = new Result<>(rechargeResult);

        } catch (Exception e) {

            log.error("后台充值处理异常, EXCEPTION:{}", e);
            result = ExceptionUtil.doExceptionService(e, RechargeResDTO.class);
        } finally {
            log.info("back recharge 耗时:{},充值结果:{}", System.currentTimeMillis() - start, result);
            rechargeValidate.release(rechargeDTO, flag);
            MDC.clear();
        }
        return result;
    }


    /**
     * 后台充值确认接口
     *
     * @param rechargeDTO 充值确认请求model
     * @return 充值结果
     */
    @Override
    public Result<RechargeResDTO> rechargeConfirm(RechargeConfirmDTO rechargeDTO, String transLogId) {
        long start = System.currentTimeMillis();
        Result<RechargeResDTO> result = null;
        boolean flag = false;

        try {
            MDC.put(SystemMarker.TRACE_LOG_ID, transLogId);

            log.info("后台充值请求参数, rechargeDTO{}", rechargeDTO);
            flag = rechargeConfirmValidate.lock(rechargeDTO);

            //1、请求对象参数校验
            ParamValidate.validateParams(rechargeDTO);

            result = rechargeConfirmValidate.validateOrder(rechargeDTO, rechargeOrderRepository, RechargeResDTO.class);
            if (result != null) {
                this.existOrderProcess(result);
                return result;
            }
            //2、请求对象转换
            RechargeBO rechargeBO = rechargeOrderRepository.loadOrderByPayNo(rechargeDTO.getPayNo());
            rechargeBO.setTradeLogId(transLogId);
            rechargeBO.setSmsCode(rechargeDTO.getSmsCode());

            //3、业务流程处理
            BackPayBO backPayBO = rechargeHandler.process(rechargeBO);

            //5、返回对象生成
            RechargeResDTO rechargeResult = BO2DTOConverter.getRechargeResDTO(backPayBO);
            result = new Result<>(rechargeResult);

        } catch (Exception e) {

            log.error("后台充值处理异常, EXCEPTION:{}", e);
            result = ExceptionUtil.doExceptionService(e, RechargeResDTO.class);
        } finally {
            log.info("back recharge 耗时:{},充值结果:{}", System.currentTimeMillis() - start, result);
            rechargeConfirmValidate.release(rechargeDTO, flag);
            MDC.clear();
        }
        return result;
    }

    /**
     * 线下充值接口
     *
     * @param rechargeDTO 充值请求对象
     * @param transLogId  日志ID
     * @return 充值结果
     */
    @Override
    public Result<RechargeResDTO> offlineRecharge(RechargeDTO rechargeDTO, String transLogId) {
        long start = System.currentTimeMillis();
        Result<RechargeResDTO> result = null;
        boolean flag = false;

        try {
            MDC.put(SystemMarker.TRACE_LOG_ID, transLogId);

            log.info("线下充值接口请求参数, rechargeDTO{}", rechargeDTO);
            flag = rechargeValidate.lock(rechargeDTO);

            //1、请求对象参数校验
            rechargeValidate.paramsValidate(rechargeDTO);
            result = rechargeValidate.validateUniqueOrder(rechargeDTO, rechargeOrderRepository, RechargeResDTO.class);
            if (result != null) {
                this.existOrderProcess(result);
                return result;
            }
            if (Strings.isNullOrEmpty(rechargeDTO.getProductChannel())) {
                log.error("线下充值请求参数校验异常, 产品渠道不能为空");
                throw new ValidateException(PayCoreErrorCode.PRODUCT_CHANNEL_IS_NULL);
            }

            if (!ServiceCodeEnum.OFFLINE_RECHARGE.getCode().equals(rechargeDTO.getServiceCode())) {
                log.error("线下充值请求参数校验异常, 服务编码传入错误");
                throw new ValidateException(PayCoreErrorCode.PARAMETER_VALID_NOT_PASS);
            }

            //2、请求对象转换
            RechargeBO rechargeBO = offlineRechargeFactory.build(rechargeDTO);

            //3、支付相关信息落地
            rechargeOrderRepository.addOrder(rechargeBO);

            //4、业务流程处理
            BackPayBO backPayBO = offlineRechargeHandler.offlineRechargeProcess(rechargeBO);

            //5、返回对象生成
            RechargeResDTO rechargeResult = BO2DTOConverter.getRechargeResDTO(backPayBO);
            result = new Result<>(rechargeResult);

        } catch (Exception e) {

            log.error("线下充值异常, EXCEPTION:{}", e);
            result = ExceptionUtil.doExceptionService(e, RechargeResDTO.class);
        } finally {
            log.info("线下充值 耗时:{},充值结果:{}", System.currentTimeMillis() - start, result);
            rechargeValidate.release(rechargeDTO, flag);
            MDC.clear();
        }
        return result;
    }

    private void existOrderProcess(Result<RechargeResDTO> result) {
        RechargeResDTO rechargeResDTO = result.getResult();
        rechargeResDTO.setRespDate(new Date());
        rechargeResDTO.setRealRtnFlag(rechargeFactory.getRealRtnFlag(rechargeResDTO.getStatus()));
    }
}
